#include "minunit.h"
#include <lcthw/hashmap.h>
#include <assert.h>
#include <lcthw/bstrlib.h>

Hashmap *map = NULL;            // create a hashmap, and set it to NULL initially
static int traverse_called = 0; // create a static variable to keep track of the number of times the traverse callback function is called

// create some bstrings
struct tagbstring test1 = bsStatic("test data 1");
struct tagbstring test2 = bsStatic("test data 2");
struct tagbstring test3 = bsStatic("test data 3");
struct tagbstring expect1 = bsStatic("the value 1");
struct tagbstring expect2 = bsStatic("the value 2");
struct tagbstring expect3 = bsStatic("the value 3");

// traverse the hashmap, each time the callback function is called, increment the traverse_called variable
// if all good, return 0
static int traverse_good_cb(HashmapNode *node)
{
    debug("KEY: %s", bdata((bstring)node->key));
    traverse_called++;
    return 0;
}

// traverse the hashmap, each time the callback function is called, increment the traverse_called variable
// if the traverse_called variable is equal to 2, return 1, otherwise return 0
// this will cause the traverse function to stop after the second call
static int traverse_fail_cb(HashmapNode *node)
{
    debug("KEY: %s", bdata((bstring)node->key));
    traverse_called++;

    if (traverse_called == 2)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

// create a function to test the creation of a hashmap
char *test_create()
{
    map = Hashmap_create(NULL, NULL); // create a hashmap, and map should no longer be NULL
    mu_assert(map != NULL, "Failed to create map.");    // check that map is not NULL, if it is NULL, print the error message

    return NULL;
}

// create a function to test the destruction of a hashmap
char *test_destroy()
{
    Hashmap_destroy(map); // destroy the hashmap

    return NULL;
}

// create a function to test the setting and getting of key-value pairs in the hashmap
char *test_get_set()
{
    int rc = Hashmap_set(map, &test1, &expect1); // set the key-value pair in the hashmap
    mu_assert(rc == 0, "Failed to set &test1");   // check that the return code is 0

    bstring result = Hashmap_get(map, &test1);    // get the value from the hashmap
    mu_assert(result == &expect1, "Wrong value for test1."); // check that the value is correct

    rc = Hashmap_set(map, &test2, &expect2);      // set the key-value pair in the hashmap
    mu_assert(rc == 0, "Failed to set &test2");   // check that the return code is 0

    result = Hashmap_get(map, &test2);            // get the value from the hashmap
    mu_assert(result == &expect2, "Wrong value for test2."); // check that the value is correct

    rc = Hashmap_set(map, &test3, &expect3);      // set the key-value pair in the hashmap
    mu_assert(rc == 0, "Failed to set &test3");   // check that the return code is 0

    result = Hashmap_get(map, &test3);            // get the value from the hashmap
    mu_assert(result == &expect3, "Wrong value for test3."); // check that the value is correct

    return NULL;
}

// create a function to test the deletion of key-value pairs from the hashmap
char *test_delete()
{
    bstring deleted = (bstring)Hashmap_delete(map, &test1); // delete the key-value pair from the hashmap
    mu_assert(deleted == &expect1, "Should get test1");    // check that the value is correct
    mu_assert(Hashmap_get(map, &test1) == NULL, "Should delete"); // check that the key-value pair is deleted

    deleted = (bstring)Hashmap_delete(map, &test2);        // delete the key-value pair from the hashmap
    mu_assert(deleted == &expect2, "Should get test2");    // check that the value is correct
    mu_assert(Hashmap_get(map, &test2) == NULL, "Should delete"); // check that the key-value pair is deleted

    deleted = (bstring)Hashmap_delete(map, &test3);        // delete the key-value pair from the hashmap
    mu_assert(deleted == &expect3, "Should get test3");    // check that the value is correct
    mu_assert(Hashmap_get(map, &test3) == NULL, "Should delete"); // check that the key-value pair is deleted

    return NULL;
}


char *all_tests()       // create a function to run all the tests
{
    mu_suite_start();   // start the test suite

    mu_run_test(test_create);   // run the test_create function
    mu_run_test(test_get_set);  // run the test_get_set function
    mu_run_test(test_delete);   // run the test_delete function
    mu_run_test(test_destroy);  // run the test_destroy function

    return NULL;
}

RUN_TESTS(all_tests);   // run all the tests